package 链表

/*
https://leetcode.cn/problems/word-break/?envType=study-plan-v2&envId=top-100-liked

139. 单词拆分
给你一个字符串 s 和一个字符串列表 wordDict 作为字典。
如果可以利用字典中出现的一个或多个单词拼接出 s 则返回 true。
注意：不要求字典中出现的单词全部都使用，并且字典中的单词可以重复使用。

示例 1：
输入: s = "leetcode", wordDict = ["leet", "code"]
输出: true
解释: 返回 true 因为 "leetcode" 可以由 "leet" 和 "code" 拼接成。

示例 2：
输入: s = "applepenapple", wordDict = ["apple", "pen"]
输出: true
解释: 返回 true 因为 "applepenapple" 可以由 "apple" "pen" "apple" 拼接成。
     注意，你可以重复使用字典中的单词。

示例 3：
输入: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"]
输出: false
*/

func wordBreak(s string, wordDict []string) bool {
	set := make(map[string]bool)
	for _, w := range wordDict { //遍历单词背包,每个单词存入map, 方便查找
		set[w] = true
	}

	dp := make([]bool, len(s)+1) //dp[j]: 以i结尾的子串,能否拆分为字典中的单词
	dp[0] = true                 //空串肯定可以被字典拼出来  逻辑上: 由于后续的状态都是由于dp[0]推出来的,所以必须为true

	for j := 1; j <= len(s); j++ { //必须先遍历背包(求的是排列数)   解释：本题为能够用背包中的单词,拼接出目标单词,即使子单词的个数对应,如果排列顺序不对,那么拼出来的也不是目标串,所以要求的是排列数
		for i := 0; i < j; i++ { //遍历物品(物品是每个j与i范围内的串)
			if dp[i] && set[s[i:j]] {
				dp[j] = true
				break
			}
		}
	}

	return dp[len(s)]
}
